#include "lock.hpp"

pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int tickets = 100;

class threadData
{
public:
    threadData(int num)
    {
        name_ = "mythread->" + std::to_string(num);
    }

    std::string name_;
};

void* Tickets(void* args)
{
    threadData* rs = static_cast<threadData*>(args);

    while(true)
    {
        {
            Mutex mymutex(&lock);
            //如果单纯使用互斥锁,由于该代码在完成抢票后没有后续的代码执行,因此线程会马上回去再次申请锁
            //此时其他线程处于挂起状态,要进行线程切换并申请锁需要时间,而这个时间里,这个锁又被刚刚的线程给申请了
            //自己拿自己用自己走了自己再拿自己再用--饥饿策略

            //针对该问题的解决方法:条件变量
            pthread_cond_wait(&cond, &lock);//只要线程持有这个lock锁，就会进入到线程库维护的等待队列
            if(tickets > 0)
            {
                std::cout << "线程: " << rs->name_.c_str() << "进行抢票,目前票数: " << tickets << std::endl;
                tickets--;
            }
            else
                break;
        }
        usleep(100);
    }
    //当票数为0时就自动退出
    pthread_exit((void*)pthread_self());
}

int main()
{
    pthread_t tip;
    pthread_mutex_t mutex;
    std::vector<pthread_t> tips;
    for(unsigned long int i = 0; i < 5; i++)
    {
        threadData* sim = new threadData(i);
        pthread_create(&tip, nullptr, Tickets, sim);//给每个线程一个地址,指向的是堆上的空间
        printf("创建出子线程: %p\n", tip);
        tips.push_back(tip);
    }
    //此时子线程全部创建完成
    int cnt = 101;
    //int cnt = tickets - 1; 共享数据，包出问题的
    while(cnt--)
    {
        pthread_cond_signal(&cond);//一次唤醒一个线程
        // pthread_cond_broadcast(&cond);//一次唤醒整个等待队列的线程
        usleep(10000);
    }
    pthread_cond_broadcast(&cond);//当cnt跑完时,等待队列还有线程没出来，统一释放

    for(int i = 0; i < 5; i++)
    {
        void* threun;
        pthread_join(tips[i], &threun);
        printf("回收子线程: %p\n", tips[i]);
    }
    return 0;
}